package omq.xml;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import omq.exception.OMQException;
import omq.api.logger.OMQLogger;
//import omq.util.Base64;
import omq.xml.element.OMQXmlElement;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import com.sun.org.apache.xerces.internal.dom.DocumentImpl;
import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;

public abstract class OMQAXmlUtil implements OMQIXmlUtil {
	protected Document dom;
	protected ArrayList domAL;
	protected Element root ;
	
	/**
	 * Constructor.
	 */
	public OMQAXmlUtil(){
		domAL = new ArrayList();
	}

	/************************************************
	 * 					READ XML METHODS			*
	 ************************************************/
	public Object parseDocument() {
		//get the root element
		Element docEle = dom.getDocumentElement();
		return handleElement(docEle);
	}
	public Object parseDocument(String xml) {
		try {
			DocumentBuilderFactory factory =
			DocumentBuilderFactory.newInstance();
			DocumentBuilder db = factory.newDocumentBuilder();
			InputSource inStream = new InputSource();
			inStream.setCharacterStream(new StringReader(xml));
			dom = db.parse(inStream);
			//get the root element
			Element docEle = dom.getDocumentElement();
			return handleElement(docEle);
		}catch (Exception e) {
			throw new OMQException(e.getMessage());
		}
	}
	public Object parseDocument(Document remoteDom) {
		//get the root element
		Element docEle = remoteDom.getDocumentElement();
		return handleElement(docEle);
	}

	public void parseXmlFile(String filePath) {
		//get the factory
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		try {
			//Using factory get an instance of document builder
			DocumentBuilder db = dbf.newDocumentBuilder();
			//parse using builder to get DOM representation of the XML file
			dom = db.parse(filePath);
		}catch(ParserConfigurationException pce) {
			throw new OMQException(pce.getMessage());
		}catch(SAXException se) {
			throw new OMQException(se.getMessage());
		}catch(IOException ioe) {
			throw new OMQException(ioe.getMessage());
		}
	}

	public void parseXmlString(String xmlString) {
		//get the factory
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		try {
			//Using factory get an instance of document builder
			DocumentBuilder db = dbf.newDocumentBuilder();
			//parse using builder to get DOM representation of the XML file
			dom = db.parse(new InputSource(new StringReader(xmlString)));
		}catch(ParserConfigurationException pce) {
			throw new OMQException(pce.getMessage());
		}catch(SAXException se) {
			throw new OMQException(se.getMessage());
		}catch(IOException ioe) {
			throw new OMQException(ioe.getMessage());
		}
	}
	/************************************************
	 * 					WRITE XML METHODS			*
	 ************************************************/
	public void createDocument(String rootElementName){
		//Document (Xerces implementation only).
		dom= new DocumentImpl();
		//Root element.
		root = dom.createElement(rootElementName);
	}
	
	public void createDocument(){
		//Document (Xerces implementation only).
		dom= new DocumentImpl();
	}
	
	public void closeDocument(){
		dom.appendChild(root);
	}
	
	public void writeXmlToFile(String filename){
		try {
			FileOutputStream fos = new FileOutputStream(filename);
			 //XERCES 1 or 2 additionnal classes.
			 OutputFormat of = new OutputFormat("XML","ISO-8859-1",true);
			 of.setIndent(1);
			 of.setIndenting(true);
			 XMLSerializer serializer = new XMLSerializer(fos,of);
			 //As a DOM Serializer
			 serializer.asDOMSerializer();
			 serializer.serialize( dom.getDocumentElement() );
			 fos.close();
		} catch (FileNotFoundException e1) {
			throw new OMQException(e1.getMessage());
		} catch (IOException e1) {
			throw new OMQException(e1.getMessage());
		}
	}

	public void addElementToRoot(OMQXmlElement element,Element parent){
		Element e = dom.createElementNS(null, element.getElementName());		
		Map attrs = element.getAttrs();
		//Inserting the attributes to the xml DOM
		if((attrs != null)&&( !attrs.isEmpty())){
			Iterator attrIter = attrs.entrySet().iterator();
			while(attrIter.hasNext()){
				Map.Entry entry = (Map.Entry) attrIter.next();
				String attrName = (String)entry.getKey();
				String attrValue = (String)entry.getValue();
				e.setAttributeNS(null,attrName,attrValue);
			}
		}
		//Inserting the node value to the element
		if(element.getNodeValue() != null && element.getNodeValue() != ""){
			Node n = dom.createTextNode(element.getNodeValue());
			e.appendChild(n);
		}
		//Checking if there are more elements to add and recursively adding the 
		//elements to the DOM.
		if(element.getElements() != null && !element.getElements().isEmpty()){
			Map innerElements = element.getElements();
			Iterator innerElementsIter = innerElements.entrySet().iterator();
			while(innerElementsIter.hasNext()){
				Map.Entry entry = (Map.Entry) innerElementsIter.next();
				OMQXmlElement innerElement = (OMQXmlElement)entry.getValue();
				//recursive call
				addElementToRoot(innerElement,e);
			}
		}
		if(parent == null){
			if(root != null){
				root.appendChild(e);
			}else{
				root = e;
			}
		}else{
			parent.appendChild(e);
		}
	}
	/************************************************
	 * 					GENERAL DOM METHODS			*
	 ************************************************/
	
	public int getIntValue(Element ele, String tagName) {
		//in production application you would catch the exception
		return Integer.parseInt(getTextValue(ele,tagName));
	}

	public String getTextValue(Element ele, String tagName) {
		String textVal = null;
		NodeList nl = ele.getElementsByTagName(tagName);
		if(nl != null && nl.getLength() > 0) {
			Element el = (Element)nl.item(0);
			textVal = el.getFirstChild().getNodeValue();
		}

		return textVal;
	}
	
	
	public Object getObject(Element ele, String tagName) {
		Object value = null;
		NodeList nl = ele.getElementsByTagName(tagName);
		if(nl != null && nl.getLength() > 0) {
			Element el = (Element)nl.item(0);
			value = el.getFirstChild().getNodeValue();
		}

		return value;
	}
	
	public Document createDocumentFromString(String xmlStr){
		try {
			DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
			//Parse the XML file and build the Document object in RAM 
			Document doc = docBuilder.parse(new InputSource(new StringReader(xmlStr)));
			return doc;
		} catch (SAXException e) {
			throw new OMQException(e.getMessage());
		} catch (IOException e) {
			throw new OMQException(e.getMessage());
		} catch (ParserConfigurationException e) {
			throw new OMQException(e.getMessage());
		}
	}
	/**
	 * This method returns the dom object's xml as String.
	 * @return - The string representation of the dom xml
	 */
	public String getDocumentAsString(){
		Writer xmlString = new StringWriter();
	    OutputFormat output = new OutputFormat( dom );
	    output.setIndenting(false);
	    XMLSerializer x = new XMLSerializer(xmlString, output);
	    try {
			x.asDOMSerializer();
			x.serialize(dom);
			return xmlString.toString();
		} catch (IOException e) {
			throw new OMQException(e.getMessage());
		}
	}
	/**
	 * This method returns the dom object's xml as String.
	 * @return - The string representation of the dom xml
	 */
	public String getDocumentAsString(Document doc){
		Writer xmlString = new StringWriter();
	    OutputFormat output = new OutputFormat( doc );
	    output.setIndenting(false);
	    XMLSerializer x = new XMLSerializer(xmlString, output);
	    try {
			x.asDOMSerializer();
			x.serialize(doc);
			return xmlString.toString();
		} catch (IOException e) {
			throw new OMQException(e.getMessage());
		}
	}
	
	public static String transformObject(Object obj){
		BASE64Encoder encoder = new BASE64Encoder();
		try {
			ByteArrayOutputStream bos = null ;
			String returnStr = "";
			if(obj != null){
				if(obj instanceof File){
					File file = (File)obj;
					returnStr = new String(encoder.encode(getBytesFromFile(file)));
				}else{
					returnStr = new String(encoder.encode(((String)obj).getBytes()));
				}
			}
			return returnStr;
		} catch (IOException e) {
			throw new OMQException(e.getMessage());
		}
	}
	
	public static Object transformByteArrayToObject(String responseMsg,String filePath,String fileName){
		if(filePath != null && !filePath.endsWith("\\")){
			filePath += "\\";
		}
		
		BASE64Decoder decoder = new BASE64Decoder();
		try {
			Object obj;
			byte[] ba = decoder.decodeBuffer(responseMsg);
			ByteArrayInputStream baInputStream = new ByteArrayInputStream(ba);
			ObjectInputStream in = new ObjectInputStream(baInputStream);
			obj = in.readObject();
			return obj;
		} catch (IOException e) {
			try {
				e.printStackTrace();
				File file = new File(filePath + fileName);
				FileOutputStream fileOutputStream = new FileOutputStream(file);
				fileOutputStream.write(decoder.decodeBuffer(responseMsg));
			} catch (FileNotFoundException e1) {
				throw new OMQException(e1.getMessage());
			} catch (IOException e1) {
				throw new OMQException(e1.getMessage());
			}
		} catch (ClassNotFoundException e) {
			throw new OMQException(e.getMessage());
		}
		return null;
	}
	
	

	public static byte[] getBytesFromFile(File file) throws IOException,OMQException {
        InputStream is = new FileInputStream(file);
    
        // Get the size of the file
        long length = file.length();
    
        if (length > Integer.MAX_VALUE) {
            // File is too large
        	throw new OMQException("The file size is too big");
        }
    
        // Create the byte array to hold the data
        byte[] bytes = new byte[(int)length];
    
        // Read in the bytes
        int offset = 0;
        int numRead = 0;
        while (offset < bytes.length
               && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
            offset += numRead;
        }
    
        // Ensure all the bytes have been read in
        if (offset < bytes.length) {
            throw new IOException("Could not completely read file " + file.getName());
        }
    
        // Close the input stream and return bytes
        is.close();
        return bytes;
    }
}
